package com.yami.shop.api.aspect;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.date.DateUnit;
import cn.hutool.crypto.SecureUtil;
import com.yami.shop.common.annotation.Idempotent;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.HttpContextUtils;
import com.yami.shop.common.util.Json;
import com.yami.shop.security.api.util.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class IdempotentAspect {

    private final Logger logger = LoggerFactory.getLogger(IdempotentAspect.class);
    private static final String USER_REQUEST = "/p/";

    private static final TimedCache<String, Integer> checkFormCache = CacheUtil.newTimedCache(DateUnit.SECOND.getMillis() * 10);


    @Around("@annotation(idempotent)")
    public Object aroundMethod(ProceedingJoinPoint pjp, Idempotent idempotent)throws Throwable {
        /**获取执行方法的参数*/
        Object[] args = pjp.getArgs();
        /**获取注解传入的超时时间*/
        int timeOut = idempotent.value();
        /**使用MD5对传入的参数进行加密*/
        String encode = getMd5Value(args);
        try{
            /** 校验是否重复提交过，如果没有，则按指定超时时间存入Redis缓存 */
            if (!"null".equals(encode) && checkForm(encode,timeOut)) {
                /**这是一个自定义的异常类，可以自己编写*/
                throw new YamiShopBindException("操作太过频繁");
            }
            /**继续执行方法*/
            return pjp.proceed();
        } catch (Exception e){
            logger.error("幂等性校验出错：" + e.getMessage(), e);
            throw e;
        }
    }

    /**
     * 查询缓存是否存在
     * @param checkCase
     * @return
     */
    public boolean checkForm(String checkCase,int timeOut){
        try {
            /**如果查询缓存不为空，返回true*/
            if (checkFormCache.containsKey(checkCase)){
                return true;
            }
            checkFormCache.put(checkCase,timeOut,DateUnit.SECOND.getMillis() * timeOut);
        }catch (Exception e){
            e.printStackTrace();
            logger.error("checkFormCache checkForm error:{}", e);
        }

        return false;
    }
    /**
     * 使用MD5对传入的参数进行加密
     * @param args
     * @return
     */
    private String getMd5Value(Object[] args) {
        String md5 = "null";
        String userId = "0";
        if(HttpContextUtils.getHttpServletRequest().getRequestURI().startsWith(USER_REQUEST) && null!=SecurityUtils.getUser()){
            userId = SecurityUtils.getUser().getUserId();
            if(!"0".equals(userId)){
                return userId;
            }
        }
        if(args.length == 0) {
            return md5;
        } else {
            StringBuilder jsonString = new StringBuilder(Json.toJsonString(args));
            if(!"0".equals(userId)){
                jsonString = jsonString.append(userId);
            }
            /**使用md5工具类对字符串进行加密*/
            md5 = SecureUtil.md5(jsonString.toString());
        }
        return md5;
    }
}
